/**
 * 
 */
package manet.crypto;

import java.math.BigInteger;
import java.util.ArrayList;
import java.util.List;

import manet.Node;

/**
 * @author stra0556
 *
 */
public class KeyCalculator {


	/**
	 * @param robotGroup The list of nodes in a group to have the same key
	 * @param c The cryptogenerator that has p, q, and g
	 */
	public static void generateIngremarssonKeys(List<Node> robotGroup, CryptoGenerator c) {

		/* Temporary list of random values */
		ArrayList<BigInteger> randoms = new ArrayList<BigInteger>();

		/* Generate a new random number and key */
		for(Node robot : robotGroup ){

			/* Create a new random number */
			BigInteger newRand = c.generateRandom(c.get_q());

			/* Get a new random number */
			randoms.add(newRand);

			/* Calculate  k = g^r mod p*/
			robot.setKey( c.get_g().modPow(newRand, c.get_p()) );
		}
		for( int j = 0; j < robotGroup.size()-1; ++j ){

			/* Make sure we keep the original key around for the first element */
			BigInteger temp = robotGroup.get(robotGroup.size()-1).getKey();

			/* Loop through and apply k_i = k_{i-1}^r_k mod p  */
			for( int i = robotGroup.size() - 1; i > 0; --i ){
				robotGroup.get(i).setKey(robotGroup.get(i-1).getKey().modPow(randoms.get(i), c.get_p()));
			}

			/* Now calculate for the first item */
			robotGroup.get(0).setKey(temp.modPow(randoms.get(0), c.get_p()));
		}
	}
  
	/**
	 * @param robotGroup The list of nodes in a group to have the same key
	 * @param c The cryptogenerator that has p, q, and g
	 */
	public static void generateBurmesterDesmedtKeys( List<Node> robotGroup, CryptoGenerator c ) {

		/* Temporary list of random values */
		ArrayList<BigInteger> random = new ArrayList<BigInteger>();

		/* The list of Z values where Z = g^r mod p */
		ArrayList<BigInteger> zValues = new ArrayList<BigInteger>();

		/* The list of x values where X = Z_{i+1}(Z_{i-1})^{-1})^r_i */
		ArrayList<BigInteger> xValues = new ArrayList<BigInteger>();

		/* Generate a new random number and key */
		for(int i = 0; i <  robotGroup.size(); ++i){

			/* Create a new random number */
			BigInteger newRand = c.generateRandom(c.get_q());

			/* Get a new random number */
			random.add(newRand);

			/* Calculate z = g^r mod p*/
			zValues.add( c.get_g().modPow(newRand, c.get_p()) );
			
		}

		/* Add the last element to the front */
		zValues.add(0, zValues.get(zValues.size()-1));

		/* Add the first element to the back */
		zValues.add( zValues.get( 1 ));

		/* Add the last element to the front */
		random.add(0, random.get(random.size()-1));

		/* Add the first element to the back */
		random.add( random.get( 1 ));
		
		for( int i = 1; i < zValues.size()-1; ++i ){
			/* Calculate Z_{i-1})^{-1}*/
			BigInteger ZInverse = zValues.get( i - 1 ).modInverse(c.get_p());

			/* Calculate Z_{i+1}(Z_{i-1})^{-1} */
			BigInteger product = ZInverse.multiply(zValues.get( i + 1 ));

			/* Calculate  (Z_{i+1}(Z_{i-1})^{-1})^r_i */
			xValues.add( product.modPow(random.get(i), c.get_p()));		
		}

		/* Remove the extra two that were added to make the ring */
		zValues.remove(0);
		zValues.remove(zValues.size()-1);
		random.remove(0);
		random.remove(random.size()-1);
			
		/* Calculate K for everyone */
		for( int i = 0; i < robotGroup.size(); ++i ){

			/* The list of Z values where Z = g^r mod p */
			ArrayList<BigInteger> ringZ = new ArrayList<BigInteger>();

			/* The list of x values where X = Z_{i+1}(Z_{i-1})^{-1})^r_i */
			ArrayList<BigInteger> ringX = new ArrayList<BigInteger>();
			
			/* Figure out all the values that should be in our circle */
			for( int j = i; j < robotGroup.size(); ++j ){
				ringZ.add(zValues.get(j));
				ringX.add(xValues.get(j));
			}
			
			/* Figure out all the values that should be in our circle */
			for( int j = 0; j < i - 1; ++j ){
				ringZ.add(zValues.get(j));
				ringX.add(xValues.get(j));
			}
			
			/* Calculate the value of K = Z_{i-1}^{nr} */
			if( i == 0 ){
				robotGroup.get(i).setKey(zValues.get(zValues.size()-1).modPow(BigInteger.valueOf(robotGroup.size()).multiply(random.get(i)), c.get_p()));
			} else {
				robotGroup.get(i).setKey(zValues.get(i-1).modPow( BigInteger.valueOf(robotGroup.size()).multiply(random.get(i)), c.get_p()));
			}
			
			/* Calculate K for all the members */
			for( int j = 0; j < ringZ.size(); ++j){
				robotGroup.get(i).setKey( (robotGroup.get(i).getKey().multiply(ringX.get(j).modPow(BigInteger.valueOf(robotGroup.size()-1-j), c.get_p()))).mod(c.get_p()));
			}
		}
	}
}



